package com.joshondesign.sdljava.events;

import java.util.*;

import com.joshondesign.sdljava.gfx.*;
import com.joshondesign.sdljava.events.RawEvents.*;
import com.joshondesign.sdljava.events.Keyboard.*;
import com.sun.org.apache.bcel.internal.classfile.Code;

/**
   The Gestures class converts raw events (mouse, keyboard, accelerometer) that may
 be platform specific, into cooked events that the developer can listen to. This can
 include basic mouse events filtered by component, or actual gestures like 'two fingered swipe'.
 It can also generate higher level events from the raw ones, like a rotation event based
 on accelerometer values, or character typing events based on the underlying keystrokes.

 developers can call Gestures.listen(Gesture,Callback) to listen for an arbitrary user action.
 Common examples:

 import static Gestures.*;
 listen(MouseClick(),callback);
 listen(KeyPressed(J,SHIFT), callback);


 */
public class Gestures {
    private static Map<Gesture,Callback> listeners;
    private static Control focusedControl;

    static {
        listeners = new HashMap<Gesture,Callback>();
    }
    
    public static void listen(Gesture gesture, Callback callback) {
        listeners.put(gesture,callback);
        gesture.setCallback(callback);
    }
    
    public static void publish(RawEvent evt) {
        for(Gesture g : listeners.keySet()) {
            if(g.isInterested(evt.getCategory())) {
                g.process(evt);
            }
        }
    }

    public static void requestFocus(Control textboxNode) {
        focusedControl = textboxNode;
    }

    public static Control getFocusedControl() {
        return focusedControl;
    }

    public static void releaseFocus(TextboxNode textboxNode) {
        focusedControl = null;
    }

    public static abstract class Gesture {

        public abstract boolean isInterested(Category cat);
        public abstract void process(RawEvent event);

        private Callback _callback;
        public void setCallback(Callback callback) {
            this._callback = callback;
        }
        public Callback getCallback() {
            return this._callback;
        }
    }

    public static class KeyEvent {
        boolean pressed;
        boolean typed;
        public Keycode keyCode;
        private boolean shiftPressed;

        public KeyEvent() {
        }

        public Keycode getKeyCode() {
            return keyCode;
        }

        public void setShiftPressed(boolean shiftPressed) {
            this.shiftPressed = shiftPressed;
        }

        public String getText() {
            String text = getKeyCode().getText();
            if(shiftPressed) {
                text = text.toUpperCase();
            }
            return text;
        }
    }

    public static class MouseEvent {
        int x;
        int y;
        int button;
        public SPoint getPoint() {
            return new SPoint(x,y);
        }
    }

    public static class ActionEvent {

        public ButtonNode target;
    }

    public static class SPoint {
        private int x;
        private int y;

        public SPoint(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public String toString() {
            return "SPoint{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }
    }



    /* gesture definitions */

    public static Gesture MouseClick() {
        return new Gesture() {
            private boolean pressed;
            public boolean isInterested(Category cat) {
                return (cat==Category.Mouse);
            }
            public void process(RawEvents.RawEvent raw) {
                RawMouseEvent evt = (RawMouseEvent) raw;
                if(evt.isPressed()) {
                    this.pressed = true;
                    return;
                }
                if(evt.isReleased()) {
                    this.pressed = false;
                    MouseEvent me = new MouseEvent();
                    me.x = evt.getX();
                    me.y = evt.getY();
                    me.button = evt.getButton();
                    this.getCallback().call(me);
                    return;
                }
                
            }
        };
    }
    
    public static Gesture KeyPressed(final Keycode targetKey, final Modifier ... mods) {
        return new Gesture() {
            private Keycode key = targetKey;
            private Modifier[] modifiers = mods;
            public boolean isInterested(Category cat) {
                return (cat == Category.Keyboard);
            }
            public void process(RawEvent raw) {
                RawKeyEvent evt = (RawKeyEvent)raw;
                if(evt.getKeyCode() == targetKey) {
                    KeyEvent e = new KeyEvent();
                    e.keyCode = evt.getKeyCode();
                    e.pressed = true;

                    for(Modifier mod:modifiers) {
                        if(mod == Modifier.Shift && !evt.shiftPressed) return;
                        if(mod == Modifier.Command && !evt.commandPressed) return;
                    }
                    this.getCallback().call(e);
                }
            }
        };
    }

    public static Gesture KeyTyped(final Keycode targetKey) {
        return new Gesture() {
            private Keycode key = targetKey;
            public boolean pressed;
            public boolean isInterested(Category cat) {
                return (cat == Category.Keyboard);
            }
            public void process(RawEvent raw) {
                RawKeyEvent evt = (RawKeyEvent)raw;
                if(evt.getKeyCode() == targetKey && evt.isPressed()) {
                    pressed = true;
                    return;
                }
                if(evt.getKeyCode() == targetKey && evt.isReleased()) {
                    pressed = false;
                    KeyEvent e = new KeyEvent();
                    e.keyCode = evt.getKeyCode();
                    e.pressed = true;
                    this.getCallback().call(e);
                }
            }
        };
    }

    public static Gesture KeyPressed(String definition) {
        String[] defs = definition.split(" ");
        List<Modifier> mods = new ArrayList<Modifier>();
        Keycode keycode = null;
        for(String def : defs) {
            if("command".equals(def.toLowerCase())) mods.add(Modifier.Command);
            if("shift".equals(def.toLowerCase())) mods.add(Modifier.Shift);
            if("j".equals(def.toLowerCase())) {
                keycode = Keycode.valueOf(def.toUpperCase());
            }
        }
        if(keycode == null) {
            throw new IllegalArgumentException("could not parse: " + definition);
        }
        return KeyPressed(keycode, mods.toArray(Modifier.values()));
    }

    public static Gesture KeyTyped() {
        return new Gesture() {
            @Override
            public boolean isInterested(Category cat) {
                return cat == Category.Keyboard;
            }

            @Override
            public void process(RawEvent raw) {
                RawKeyEvent evt = (RawKeyEvent)raw;
                if(evt.isReleased()) {
                    KeyEvent e = new KeyEvent();
                    e.keyCode = evt.getKeyCode();
                    e.typed = true;
                    this.getCallback().call(e);
                }
            }
        };
    }

    public static Gesture KeyPressed() {
        return new Gesture() {
            @Override
            public boolean isInterested(Category cat) {
                return cat == Category.Keyboard;
            }

            @Override
            public void process(RawEvent raw) {
                RawKeyEvent evt = (RawKeyEvent)raw;
                if(evt.isPressed()) {
                    KeyEvent e = new KeyEvent();
                    e.keyCode = evt.getKeyCode();
                    e.pressed = true;
                    e.setShiftPressed(evt.shiftPressed);
                    this.getCallback().call(e);
                }
            }
        };
    }

    public static Gesture Action(final ButtonNode button) {
        return new Gesture() {
            @Override
            public boolean isInterested(Category cat) {
                return cat == Category.Control;
            }

            @Override
            public void process(RawEvent event) {
                RawControlEvent e = (RawControlEvent) event;
                if(e.getTarget() == button) {
                    ActionEvent actionEvent = new ActionEvent();
                    actionEvent.target = button;
                    this.getCallback().call(actionEvent);
                }
            }
        };
    }


}
